import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Vector;

public class ReadFile {
	private String inputDirectory = "emails/";
	private String oracleDirectory = "oracles/";
	private String dictionaryDirectory = "dict/";

	/**
	 * A dictionary of accepted words.
	 */
	private HashSet<String> dictionary;

	/**
	 * A dictionary of stop words. Stop words are those words with little
	 * meaning to a sentence but which occur frequently in language.
	 */
	private HashSet<String> stopwords;
	private PrintWriter out;

	public static void main(String[] args) {

		long start;
		long elapsed;

		start = System.currentTimeMillis();

		new ReadFile();

		elapsed = System.currentTimeMillis() - start;

		// Record the run-time for all algorithms
		System.out.println("Time taken: " + elapsed);

	}

	public ReadFile() {
		// Initialise the dictionaries

		try {
			out = new PrintWriter(new FileWriter("output/results.txt"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		dictionary = ReadDictionary("dictionary", new HashSet<String>());
		stopwords = ReadDictionary("stopwords", new HashSet<String>());

		// Load the training data
		Vector<DataBean> training;
		training = fileImporter(oracleDirectory, "train.dat");
		String trainingSummery = emailSummerizer(training);

		// Load the testing data
		Vector<DataBean> testingSet = fileImporter(oracleDirectory, "test.dat");
		String testSummery = emailSummerizer(testingSet);

		// Evaluate Machine Learning Algorithms
		machineLearning(training, trainingSummery, testingSet, testSummery);
		out.close();
	}

	/**
	 * Loads the dictionary specified by filename into the object dictionary
	 * 
	 * @param filename
	 *            - the name of the dictionary
	 * @param dictionary
	 *            - the dictionary object
	 * @return the updated dictionary
	 */
	public HashSet<String> ReadDictionary(String filename,
			HashSet<String> dictionary) {
		BufferedReader in = null;

		try {
			in = new BufferedReader(new FileReader(dictionaryDirectory
					+ filename));
			while (in.ready()) {
				String newWord = in.readLine();

				// add the word to the dictionary
				dictionary.add(newWord);
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return dictionary;
	}

	/**
	 * Convert the a dataset of emails into a summarised form for processing by
	 * Weka
	 * 
	 * @param dataset
	 *            - a dataset of emails
	 * @return string summary of the dataset
	 */
	private String emailSummerizer(Vector<DataBean> dataset) {

		StringBuffer trainingString = new StringBuffer(
				"@RELATION codesnippets\n\n" + "@ATTRIBUTE length\tNUMERIC\n"
						+ "@ATTRIBUTE num_words\tNUMERIC\n"
						+ "@ATTRIBUTE non_dictionary\tNUMERIC\n"
						+ "@ATTRIBUTE freq_stopwords\tNUMERIC\n"
						+ "@ATTRIBUTE non_charBased\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_click\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_viagra\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_http\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_make\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_address\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_our\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_internet\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_order\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_business\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_free\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_your\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_you\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_money\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_credit\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_000\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_mail\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_receive\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_will\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_invoice\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_payment\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_meeting\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_investment\tNUMERIC\n"
						+ "@ATTRIBUTE word_freq_thanks\tNUMERIC\n"
						+ "@ATTRIBUTE char_freq_#\tNUMERIC\n"
						+ "@ATTRIBUTE char_freq_$\tNUMERIC\n"
						+ "@ATTRIBUTE char_freq_!\tNUMERIC\n"
						+ "@ATTRIBUTE char_freq_[\tNUMERIC\n"
						+ "@ATTRIBUTE char_freq_(\tNUMERIC\n"
						+ "@ATTRIBUTE char_freq_;\tNUMERIC\n"
						+ "@ATTRIBUTE char_freq_@\tNUMERIC\n"
						+ "@ATTRIBUTE class\t{0, 1}\n\n@DATA\n");

		for (DataBean email : dataset) {
			trainingString.append(email + "\n");
		}
		return trainingString.toString();

	}

	/**
	 * Loads the email specified by filename into a new DataBean
	 * 
	 * @param inputDirectory
	 *            - the location of the email
	 * @param filename
	 *            - the email filename
	 * @return the DataBean containing the loaded email
	 */
	private DataBean emailImporter(String inputDirectory, String filename) {
		BufferedReader in = null;
		StringBuffer email = null;
		try {
			in = new BufferedReader(new FileReader(inputDirectory + filename));
			email = new StringBuffer();
			while (in.ready()) {
				email.append(in.readLine().toString());
			}
			return new DataBean(email.toString(), filename, dictionary,
					stopwords);
		} catch (Exception e) {
			System.out.println("File: " + filename + " did not read");
		}
		return null;
	}

	/**
	 * Loads the email dataset specified by the dataType
	 * 
	 * @param oracleDirectory
	 *            - the location of the dataset
	 * @param dataType
	 *            - the type of email (train or test set)
	 * @return the dataset loaded as a Vector<DataBean>
	 */
	private Vector<DataBean> fileImporter(String oracleDirectory,
			String dataType) {
		Vector<DataBean> fileList = new Vector<DataBean>();
		BufferedReader in = null;
		String[] test = null;
		String filename;
		DataBean temp;

		try {
			in = new BufferedReader(new FileReader(oracleDirectory + dataType));
			while (in.ready()) {
				test = in.readLine().split("\t");
				filename = "";
				if (Integer.parseInt(test[104]) < 10) {
					filename = "00" + test[104] + ".txt";

				} else if (Integer.parseInt(test[104]) < 100) {
					filename = "0" + test[104] + ".txt";

				} else {
					filename = test[104] + ".txt";
				}
				temp = emailImporter(inputDirectory, filename);
				temp.setClassification(test[103]);
				fileList.add(temp);
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return fileList;

	}

	/**
	 * Trains and test each algorithm on the email datasets
	 * 
	 * @param training
	 *            - the set of training emails
	 * @param trainingSummery
	 *            - the formatted training email dataset
	 * @param testingSet
	 *            - the set of test emails
	 * @param testSummery
	 *            - the formatted test email dataset
	 */
	private void machineLearning(Vector<DataBean> training,
			String trainingSummery, Vector<DataBean> testingSet,
			String testSummery) {

		// Record the format of the output
		// System.out.println("Algorithm");
		// System.out.println("Run Time");
		System.out.println("False Negative Rate");
		System.out.println("False Positive Rate");
		System.out.println("True Negative Rate");
		System.out.println("True Positive Rate:");

		MachineLearning ml;

		/*
		 * For each algorithm train the model and then test the model on novel
		 * data
		 */
		for (int i = 1; i < 22; i++) {
			// for (int j = 10; j <= 500; j += 10) {

			ml = new MachineLearning();

			long start = System.currentTimeMillis();

			// Train the model
			String name = ml.mlSetup(trainingSummery, i, 0);

			// Record the algorithm name
			// System.out.println(name);

			// Test the model
			ml.calculateML(testingSet, testSummery);

			long end = System.currentTimeMillis();

			// Record the run-time
			// System.out.println((end - start));

			// report(testingSet);
			out.println((end - start) + "," + name + ","
					+ ml.getEvaluation().fMeasure(0) + ","
					+ ml.getEvaluation().fMeasure(1));
//			System.out.println((end - start) + "," + name + ","
//					+ ml.getEvaluation().fMeasure(0) + ","
//					+ ml.getEvaluation().fMeasure(1));
			// }

		}
	}

	@SuppressWarnings("unused")
	private void report(Vector<DataBean> testingSet) {
		int wrongClass_email = 0; // emails that were incorrectly classified as
									// spam
		int correctClass_email = 0; // emails that were correctly classified as
									// non-spam
		int wrongClass_spam = 0; // spam that was incorrectly classified as
									// non-spam
		int correctClass_spam = 0; // spam that was correctly classified as spam

		for (DataBean test : testingSet) {
			String true_class = test.getClassification();
			String predicted_class = test.getPrediction();
			if (true_class.equals(predicted_class)) {
				if (true_class.equals("1")) {
					correctClass_spam++;
				} else {
					correctClass_email++;
				}
			} else if (true_class.equals("1")) {
				wrongClass_spam++;
			} else {
				wrongClass_email++;
			}

		}

		System.out.println("Number of correctly Classified Spam: "
				+ correctClass_spam);
		System.out.println("Number of incorrectly Classified Spam: "
				+ wrongClass_email);
		System.out.println("Number of correctly Classified non-Spam: "
				+ correctClass_email);
		System.out.println("Number of incorrectly Classified non-Spam: "
				+ wrongClass_spam);
	}
}
